package com.xunsiya.service.module.media.impl;

import com.xunsiya.common.config.RequestUtils;
import com.xunsiya.common.config.SftpConfig;
import com.xunsiya.common.config.util.*;
import com.xunsiya.module.device.dao.DeviceMapper;
import com.xunsiya.module.device.vo.Device;
import com.xunsiya.module.media.dao.MediaMapper;
import com.xunsiya.module.media.vo.Image;
import com.xunsiya.module.media.vo.Media;
import com.xunsiya.modules.algorithm.AbstractRecognize;
import com.xunsiya.modules.algorithm.AbstractResultParser;
import com.xunsiya.modules.algorithm.OpType;
import com.xunsiya.modules.algorithm.ResultVo;
import com.xunsiya.modules.algorithm.fp.FpResultParse;
import com.xunsiya.modules.algorithm.fp.FpSingleRecog;
import com.xunsiya.modules.algorithm.fp.FpTools;
import com.xunsiya.modules.algorithm.fp.FpUpdateDb;
import com.xunsiya.modules.algorithm.fp.vo.FileFpResultVO;
import com.xunsiya.modules.algorithm.fp.vo.MatchFP;
import com.xunsiya.modules.algorithm.fp.vo.MatchLib;
import com.xunsiya.modules.algorithm.rpc.RPCReturnCode;
import com.xunsiya.modules.algorithm.rpc.RpcConstant;
import com.xunsiya.service.module.media.MediaService;
import com.xunsiya.system.dao.DictMapper;
import com.xunsiya.system.vo.Dict;
import com.xunsiya.system.vo.User;
import com.xunsiya.tools.common.config.PathUtils;
import com.xunsiya.tools.common.msg.Msg;
import com.xunsiya.tools.common.pagination.ForePage;
import com.xunsiya.tools.common.pagination.PagedUtil;
import com.xunsiya.tools.common.string.StringUtil;
import com.xunsiya.tools.file.cryptography.FileMD5Util;
import com.xunsiya.tools.file.type.FileTypeJudge;
import com.xunsiya.tools.file.type.MediaFileType;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.IOException;
import java.util.*;


/**
 * 图像服务实现
 *
 * @version 1.0
 * @author: xml
 * @date: 2018/10/31
 */
@Service("mediaService")
public class MediaServiceImpl implements MediaService {

    private static Logger logger = Logger.getLogger(MediaServiceImpl.class);

    @Value("${path.vfp-path}")
    private String genSiftCmd;

    @Value("${path.sift-save-path}")
    private String siftSavePath;

    @Value("${path.media-sava-path}")
    private String mSavePath;

    @Value("${path.base-url}")
    private String baseUrl;

    @Value("${path.base-path}")
    private String basePath;

    @Value("${media.zoom-width}")
    private Integer zoomWidth;

    @Value("${media.zoom-height}")
    private Integer zoomHeight;

    /**
     * 媒体文件目录
     */
    private static final String MEDIA_PATH_SUFFIX = "/MEDIA/";
    /**
     * 截图目录
     */
    private static final String CAPTURE_PATH_SUFFIX = "/CAPTURE/";
    /**
     * 截图目录
     */
    private static final String MEDIA_PATH_EXTRA = "/EXTRA/";

    /**
     * 图片类型名称
     */
    public static final String IMAGE_TYPE = "image_type";

    /**
     * 文件存放目录
     */
    private static String mediaSavePath = null;

    @Autowired
    private MediaMapper mediaMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DictMapper dictMapper;

    @Autowired
    private SftpConfig sftpConfig;


    /**
     * 添加指纹
     * 步骤：保存文件、获取指纹、信息入库、增加到指纹库
     *
     * @param vo 图像
     * @return
     */
    @Override
    public Msg save(Media vo) {
        File fpFile = new File(vo.getMediaPath());
        if (!fpFile.exists()) {
            return new Msg(false, "输入的文件不存在！");
        }

        try {
            if (!FileTypeJudge.isImage(vo.getMediaPath())) {
                return new Msg(false, "上传的图片不符合要求，请重新上传！");
            }
        } catch (Exception e) {
            return new Msg(false, "上传的图片不符合要求，请重新上传！");
        }

        return loadFP(fpFile, vo.getCompanyId(), vo.getType(), vo);
    }

    /**
     * 分页查询指纹信息
     *
     * @param media     图像
     * @param pageIndex 每页开始行数
     * @param pageSize  每页大小
     * @return
     */
    @Override
    public ForePage<Media> querySiftForPage(Media media, int pageIndex, int pageSize) {
        int count = mediaMapper.queryCount(media);
        List<Media> list = mediaMapper.queryForPage(media, PagedUtil.getStartIndex(pageIndex, pageSize), pageSize);
        if (list != null && list.size() > 0) {
            for (Media vo : list) {
                // 获取缩放图片长宽
                String style = "border-radius: 10px; " + ImgUtil.zoomImage(vo.getMediaPath(), zoomWidth, zoomHeight);
                vo.setStyle(style);
                vo.setMediaPath(vo.getMediaPath().replace(basePath + mSavePath, PathUtils.getBaseUrl() + mSavePath));
                if (!StringUtil.isEmpty(vo.getCapturePath())) {
                    vo.setCapturePath(vo.getCapturePath().replace(basePath + mSavePath, PathUtils.getBaseUrl() + mSavePath));
                }
            }
        }
        return new ForePage<>(count, pageIndex, list);
    }

    /**
     * 通过PATH查询
     *
     * @param deviceId 设备编号
     * @param path
     * @return
     */
    @Override
    public List<Image> searchByPath(String deviceId, String path) {
        // 根据紫万账号查询设备信息
        Device device = deviceMapper.findByGuid(deviceId);
        if (device == null) {
            logger.info("设备信息不存在");
            return null;
        }
        try {
            String newPath;
            // 文件
            File file = new File(path);
            // MD5
            String fileMd5 = FileMD5Util.getMD5FromFile(file);
            //获取文件后缀
            String suffix = path.substring(path.indexOf("."), path.length());
            newPath = basePath + mSavePath + fileMd5 + suffix;
            logger.info("文件路径为：" + newPath);
            // 复制文件
            FileUtils.copyFile(file, new File(newPath));
            Object[] params = new Object[]{newPath.replaceFirst(
                    basePath + mSavePath, basePath + mSavePath)};
            if (logger.isInfoEnabled()) {
                logger.info("进行数据扫描,扫描的文件路径为：" + Arrays.toString(params));
            }
            String resultFile = basePath + mSavePath + fileMd5 + ConstantInfo.ANALYSIS_TXT_SUFFIX;
            AbstractRecognize fpSingleRecog;
            MediaFileType fileType;
            if (FileTypeJudge.isImage(path)) {
                fileType = MediaFileType.IMAGE;
            } else if (FileTypeJudge.isVideo(path)) {
                fileType = MediaFileType.VIDEO;
            } else {
                return null;
            }
            fpSingleRecog = new FpSingleRecog(fileType, RpcConstant.VFP_FILTER_MODULENAME, newPath, fileMd5, new String[]{device.getCompanyId() + ""}, resultFile);
            // 解析
            int fpCode = fpSingleRecog.processFile();
            logger.info("返回码为：" + fpCode);
            if (fpCode < RPCReturnCode.SUCCESS.getValue()) {
                return null;
            }
            // 结果解析初始化
            AbstractResultParser fpResultParse = new FpResultParse();
            // 结果解析
            ResultVo<FileFpResultVO> resultVo = fpResultParse.parse(resultFile, 300);
            List<FileFpResultVO> dataList = (List<FileFpResultVO>) resultVo.getData();
            List<Image> fpList = new ArrayList<>();
            Image image;
            for (FileFpResultVO e : dataList) {
                for (MatchLib matchLib : e.getMatchLibs()) {
                    for (MatchFP matchFP : matchLib.getMatchFPs()) {
                        image = new Image();
                        String md5Path = matchFP.getFpId();
                        logger.info("返回结果的md5：" + md5Path);
                        Media fp = mediaMapper.queryMediaByMd5(md5Path, device.getCompanyId());
                        fp.setMediaPath(fp.getMediaPath().replace(basePath, sftpConfig.getBaseUrl()));
                        logger.info("返回结果的mediaPath：" + fp.getMediaPath());
                        fp.setCapturePath(fp.getCapturePath().replace(basePath, baseUrl));
                        logger.info("返回结果的capturePath：" + fp.getCapturePath());
                        fp.setScore(matchFP.getScore());
                        logger.info("返回结果的分数：" + matchFP.getScore());
                        image.setName(fp.getMediaName());
                        // 查询图片类型
                        Dict dict = dictMapper.findByValue(IMAGE_TYPE, fp.getType());
                        image.setType(dict.getLabel());

                        image.setContent(fp.getMediaContent());
                        if (!StringUtil.isEmpty(fp.getExtraInfo())
                                && !fp.getType().equals(ConstantInfo.MediaFileType.TXT.getValue())) {
                            image.setContent(fp.getExtraInfo().replace(PathUtils.getBasePath(), sftpConfig.getBaseUrl()));
                        }

                        fpList.add(image);
                    }
                }
            }

            // 删除文件
            FileUtils.deleteQuietly(file);

            return fpList;
        } catch (Exception e) {
            logger.error("扫描出错，");
            logger.error("出错的内容：" + e.toString());
        }
        return null;
    }

    /**
     * 删除指纹
     *
     * @param id
     * @param companyId 公司编号
     * @return
     */
    @Override
    public Msg deleteSift(int id, int companyId) {
        Media vo = mediaMapper.querySiftById(id);
        if (vo != null) {
            //删除对应的文件数据
            if (!StringUtil.isEmpty(vo.getMediaPath())) {
                new File(vo.getMediaPath()).delete();
            }
            if (!StringUtil.isEmpty(vo.getCapturePath())) {
                new File(vo.getCapturePath()).delete();
            }
            AbstractRecognize fp = new FpUpdateDb(RpcConstant.VFP_FILTER_MODULENAME, OpType.DELETE, Integer.toString(companyId), vo.getMediaPath(), vo.getMd5());
            List<Map<String, Object>> o = null;
            try {
                o = fp.updateDB();
            } catch (Exception e) {
                logger.info("", e);
            }

            if (CollectionUtils.isEmpty(o)) {
                logger.info("processMediaSift报错:XMLRPC无返回");
                return new Msg(false, "processMediaSift报错:XMLRPC无返回");
            }

            Map<String, Object> map = o.get(0);
            if (CollectionUtils.isEmpty(map)) {
                logger.info("processMediaSift报错:XMLRPC无返回");
                return new Msg(false, "processMediaSift报错:XMLRPC无返回");
            }

            int result = Integer.parseInt(map.get("result").toString());
            if (result != RPCReturnCode.SUCCESS.getValue()) {
                logger.info("processMediaSift报错:" + map.get("url"));
                return new Msg(false, map.get("url") + "");
            } else {
                logger.info("特征库删除成功!");
            }
            mediaMapper.deleteSiftById(id);
            return new Msg(true, "操作成功", vo.getMd5());
        } else {
            return new Msg(true, "已删除");
        }
    }

    /**
     * 修改指纹
     *
     * @param media 图像
     * @return
     */
    @Override
    public Msg update(Media media) {
        /**
         * 当前登录用户
         */
        User user = RequestUtils.getSessionAttribte(User.SYS_LOGIN_INFO);

        // 移动附件
        if (!StringUtil.isEmpty(media.getExtraInfo())) {
            File extFile = new File(media.getExtraInfo());
            if (extFile.exists()) {
                // 上传图片
                String extPath = moveFile(extFile, getMediaSaveDir() + media.getCompanyId() + MEDIA_PATH_EXTRA);
                media.setExtraInfo(extPath);
            }
        }

        //通过id查询
        Media vo = mediaMapper.querySiftById(media.getId());
        //如果能查到，重新设值
        if (vo != null && media != null) {
            vo.setMediaName(media.getMediaName());
            vo.setUpdateTime(new Date());
            vo.setUpdateBy(user.getId());
            vo.setMediaContent(media.getMediaContent());
            vo.setType(media.getType());
            mediaMapper.update(vo);
            return new Msg(true, "修改成功");
        } else {
            return new Msg(false, "修改失败");
        }

    }

    /**
     * 入库
     *
     * @param vo 图像
     * @return
     */
    public Msg insert(Media vo) {

        try {
            mediaMapper.saveSift(vo);
        } catch (Exception e) {
            logger.error("错误", e);
            return new Msg(false, "入库失败");
        }
        return new Msg(true, "操作成功");
    }

    /**
     * 加载指纹并入库
     *
     * @param mediaFile 图像文件
     * @param companyId 公司编号
     * @param type      文件类型
     * @param vo        图像
     * @return
     */
    private Msg loadFP(File mediaFile, int companyId, String type, Media vo) {
        //校验文件是否是图片或视频,而非目录或者其他文件
        Msg message = checkFileType(mediaFile);
        if (message != null) {
            return message;
        }
        //文件路径
        String filePath = mediaFile.getAbsolutePath();
        // 媒体文件存储目录
        String destMediaSaveDir = getMediaSaveDir() + companyId + MEDIA_PATH_SUFFIX;
        if (!new File(destMediaSaveDir).exists()) {
            new File(destMediaSaveDir).mkdirs();
        }

        logger.info("开始生成md5值:" + filePath);
        String md5 = "";
        try {
            md5 = FileMD5Util.getMD5FromFile(mediaFile);
        } catch (Exception e1) {
            logger.error("错误", e1);
        }

        // 移动附件
        if (!StringUtil.isEmpty(vo.getExtraInfo())) {
            File extFile = new File(vo.getExtraInfo());
            if (extFile.exists()) {
                String extPath = moveFile(extFile, getMediaSaveDir() + companyId + MEDIA_PATH_EXTRA);
                vo.setExtraInfo(extPath);
            }
        }

        //将文件移到指定路径下,并且重命名
        filePath = moveFile(mediaFile, destMediaSaveDir);
        //当前登录用户
        User user = RequestUtils.getSessionAttribte(User.SYS_LOGIN_INFO);
        vo.setMediaPath(filePath);
        //状态可用
        vo.setStatus("1");
        vo.setMd5(md5);
        vo.setCreateTime(new Date());
        vo.setMediaContent(vo.getMediaContent());
        vo.setCreateBy(user.getId());
        // 生成指纹
        logger.info("开始生成指纹:" + filePath);
        String destSiftSaveDir = siftSavePath;
        if (!new File(destSiftSaveDir).exists()) {
            new File(destSiftSaveDir).mkdirs();
        }
        //指纹存储路径
        String siftFileSavedPathName = destSiftSaveDir + md5 + ConstantInfo.SIFT_SUFFIX;
        //生成指纹
        if (!new File(siftFileSavedPathName).exists()) {
            String msg = "生成指纹失败!";
            try {
                int genSiftFlag = FpTools.genPicFP(filePath, genSiftCmd);
                if (genSiftFlag != 1) {
                    return new Msg(false, msg);
                }
            } catch (Exception e) {
                logger.error("错误", e);
                return new Msg(false, msg);
            }
        }
        vo.setSiftPath(siftFileSavedPathName);
        //调用接口生成指纹
        Msg algoMessage = invokeAlgorithm(filePath, companyId, md5);
        if (algoMessage != null) {
            return algoMessage;
        }
        //生成原始视频截图,并获取原始视频信息
        genVideoCaptureImg(filePath, companyId, vo, md5);
        vo.setCompanyId(companyId);
        vo.setMediaName(vo.getMediaName());
        vo.setType(type);
        // 入库
        List<Media> qmd = mediaMapper.querySiftByMd5(md5, companyId);
        Msg msg;
        if (qmd.size() == 0) {
            msg = insert(vo);
            logger.info("是否入库成功： " + msg.isSuccess());
        } else {
            //状态可用
            qmd.get(0).setStatus("1");
            mediaMapper.updateSiftStatus(qmd.get(0));
            msg = new Msg(true);
            msg.setMsg("操作成功");
        }
        return msg;
    }

    /**
     * 获取文件存在路径
     *
     * @return
     */
    private String getMediaSaveDir() {
        if (mediaSavePath == null) {
            mediaSavePath = basePath + mSavePath;
            File dirFile = new File(mediaSavePath);
            if (!dirFile.exists()) {
                dirFile.mkdirs();
            }
            String separator = "/";
            String separator2 = "\\";
            if (!mediaSavePath.endsWith(separator) && !mediaSavePath.endsWith(separator2)) {
                mediaSavePath += separator;
            }
        }
        return mediaSavePath;
    }

    /**
     * 校验文件是否是图片或视频
     *
     * @param mediaFile 图像文件
     * @return
     */
    private Msg checkFileType(File mediaFile) {
        // 合法判断:需要是一个图片或者视频文件，而非目录或者其他文件
        if (mediaFile == null || !mediaFile.exists() || mediaFile.isDirectory()) {
            logger.warn("待加载的文件不存在或者是一个目录");
            return new Msg(false, "待加载的文件不存在或者是一个目录");
        }
        try {
            if (!FileTypeJudge.isImage(mediaFile.getAbsolutePath()) && !FileTypeJudge.isVideo(mediaFile.getAbsolutePath())) {
                logger.warn("待加载的文件[" + mediaFile.getAbsolutePath() + "]不是图片也不是视频");
                return new Msg(false, "待加载的文件不是图片也不是视频");
            }
        } catch (IOException e) {
            logger.error("错误", e);
        }
        return null;
    }


    /**
     * 生成视频截图
     *
     * @param filePath  文件路径
     * @param companyId 公司编号
     * @param vo        图像
     * @param md5       MD5值
     */
    private void genVideoCaptureImg(String filePath, int companyId, Media vo, String md5) {
        try {
            if (FileTypeJudge.isVideo(filePath)) {
                // 视频类型
                vo.setMediaType("1");
                String destCaptureSaveDir = getMediaSaveDir() + companyId + CAPTURE_PATH_SUFFIX;
                if (!new File(destCaptureSaveDir).exists()) {
                    new File(destCaptureSaveDir).mkdirs();
                }
                // 开始截图
                String captureImgFileName = destCaptureSaveDir + md5 + ".jpg";
                if (!new File(captureImgFileName).exists()) {
                    String errMsg = VideoCaptureUtil.captureImageAndGetBasicInfo(filePath, captureImgFileName, 10);
                    if (!StringUtil.isEmpty(errMsg)) {
                        logger.error("文件[" + filePath + "]截图失败!");
                    } else {
                        vo.setCapturePath(captureImgFileName);
                    }
                } else {
                    logger.info("文件[" + filePath + "]已存在历史截图，复用历史截图!");
                    vo.setCapturePath(captureImgFileName);
                }
            } else {
                // 图片类型
                vo.setMediaType("0");
                vo.setCapturePath(filePath);
            }
        } catch (IOException e) {
            logger.error("错误", e);
        }
    }

    /**
     * 调用算法生成指纹
     *
     * @param filePath  文件路径
     * @param companyId 公司编号
     * @param md5       MD5
     * @return
     */
    private Msg invokeAlgorithm(String filePath, int companyId, String md5) {
        AbstractRecognize fp = new FpUpdateDb(RpcConstant.VFP_FILTER_MODULENAME, OpType.ADD, Integer.toString(companyId), filePath, md5);
        List<Map<String, Object>> o = null;
        try {
            o = fp.updateDB();
        } catch (Exception e) {
            logger.error("", e);
        }
        if (CollectionUtils.isEmpty(o)) {
            logger.info("processMediaSift报错:XMLRPC无返回");
            return new Msg(false, "processMediaSift报错:XMLRPC无返回");
        }

        Map<String, Object> map = o.get(0);
        if (CollectionUtils.isEmpty(map)) {
            logger.info("processMediaSift报错:XMLRPC无返回");
            return new Msg(false, "processMediaSift报错:XMLRPC无返回");
        }

        int result = Integer.parseInt(map.get("result").toString());
        if (result != RPCReturnCode.SUCCESS.getValue()) {
            logger.info("processMediaSift报错:" + map.get("result"));
            if (result != RPCReturnCode.OBJ_EXIST.getValue()) {
                //已经添加此指纹
                return new Msg(false, map.get("url") + "");
            } else {
                return new Msg(false, "指纹已存在");
            }
        } else {
            logger.info("特征库入库成功!");
        }
        return null;
    }

    /**
     * @param mediaFile        待移动图像文件
     * @param destMediaSaveDir 图像保存目录
     */
    private String moveFile(File mediaFile, String destMediaSaveDir) {
        try {
            String destFilePath = destMediaSaveDir + mediaFile.getName();
            FileUtils.moveFile(mediaFile, new File(destFilePath));

            // 新的媒体文件路径
            mediaFile = new File(destFilePath);
            return destFilePath;
        } catch (Exception e) {
            logger.error("错误", e);
        }
        return null;
    }
}
